package emulator.unittest.assembler;


import static org.junit.Assert.*;

import org.junit.After;
import org.junit.Before;
import org.junit.Test;

import emulator.assembler.DataDescription;
import emulator.assembler.DataDescriptionTable;
import emulator.assembler.Instruction;
import emulator.assembler.SymbolTable;
import emulator.hardware.nmos6502.commands.BIT;
import emulator.hardware.nmos6502.commands.BRK;
import emulator.hardware.nmos6502.operands.Absolute;
import emulator.hardware.nmos6502.operands.NoOperand;
import emulator.util.MemoryPtr;

public class DataDescriptionTableTest {

	private final static int max_range = 32;
	private final static int initial_gran = 8; 
	private DataDescriptionTable table = null;
	
	@Before
	public void setUp() throws Exception {
		
		table = new DataDescriptionTable(0, max_range-1, initial_gran);
	}

	@After
	public void tearDown() throws Exception {
		table = null;
	}

	@Test
	public void testInitialize()
	{
		assertEquals(getInitialTableSize(), table.size());
		for (int i = 0; i < getInitialTableSize(); i++)
			assertIsUntouched(i, i*initial_gran);
	}

	private void assertIsUntouched(int i, int address) {
		DataDescription data = table.getDescriptionForIndex(i);
		assertNotNull(data);
		assertEquals(address, data.getAddress());
		assertFalse(data.isCode());
		assertFalse(data.isJumpTarget());
		assertEquals(0, data.getAccessCount());
	}

	@Test
	public void testInsertBreakFront()
	{
		byte[] memory = { 0 };
		MemoryPtr mem_ref = new MemoryPtr(memory, 0);
		Instruction instruction = new Instruction(mem_ref, new BRK(), new NoOperand(), null);
		table.insertInstruction(0, instruction);
		table.reindex();
		assertEquals(getInitialTableSize()+1, table.size());
		checkData(table.getDescriptionForIndex(0), 0, 1, true, false);
		checkData(table.getDescriptionForIndex(1), 1, initial_gran-1, false, false);
		for (int i = 1; i < getInitialTableSize(); i++)
			assertIsUntouched(i+1, i*initial_gran);
	}

	private void checkData(DataDescription actual_data, int expected_address,
			int expected_length, boolean expected_iscode,
			boolean expected_isjumptarget) {
		assertNotNull(actual_data);
		assertEquals(expected_address, actual_data.getAddress());
		assertEquals(expected_length, actual_data.getLength());
		assertEquals(expected_iscode, actual_data.isCode());
		assertEquals(expected_isjumptarget, actual_data.isJumpTarget());
	}
	
	@Test
	public void testShiftEntryBegin()
	{
		DataDescription data = table.getDescriptionForIndex(0);
		table.shiftEntryBegin(data, 1);
		checkData(data, 1, initial_gran-1, false, false);
	}

	@Test
	public void testInsertCodeBlock()
	{
		table.shiftEntryBegin(table.getDescriptionForIndex(0), 1);
		table.insertCodeBlock(0, 1);
		checkData(table.getDescriptionForAddress(0), 0, 1, true, false);
	}
	
	@Test
	public void testInsertTwoInstructions()
	{
		byte[] memory = { 0 };
		MemoryPtr mem_ref = new MemoryPtr(memory, 0);
		Instruction instruction = new Instruction(mem_ref, new BRK(), new NoOperand(), null);
		table.insertInstruction(0, instruction);
		table.insertInstruction(2, instruction);
		table.reindex();
		assertEquals(getInitialTableSize()+3, table.size());
		checkData(table.getDescriptionForIndex(0), 0, 1, true, false);
		checkData(table.getDescriptionForIndex(1), 1, 1, false, false);
		checkData(table.getDescriptionForIndex(2), 2, 1, true, false);
		checkData(table.getDescriptionForIndex(3), 3, 5, false, false);
		for (int i = 1; i < getInitialTableSize(); i++)
			assertIsUntouched(i+3, i*initial_gran);
	}
	
	@Test
	public void testInsertMergeInstructions()
	{
		byte[] memory = { 0, 1, 2 };
		MemoryPtr mem_ref = new MemoryPtr(memory, 0);
		Instruction instruction1 = new Instruction(mem_ref, new BRK(), new NoOperand(), null);
		Instruction instruction3 = new Instruction(mem_ref, new BIT(), new Absolute(), new SymbolTable());
		table.insertInstruction(0, instruction1);
		table.insertInstruction(2, instruction1);
		table.insertInstruction(0, instruction3);
		table.reindex();
		assertEquals(getInitialTableSize()+1, table.size());
		checkData(table.getDescriptionForIndex(0), 0, 3, true, false);
		checkData(table.getDescriptionForIndex(1), 3, 5, false, false);
		for (int i = 1; i < getInitialTableSize(); i++)
			assertIsUntouched(i+1, i*initial_gran);
	}
	
	/***********/
	/* Helpers */
	/***********/

	private int getInitialTableSize() {
		return (max_range+initial_gran-1)/initial_gran;
	}
	
}
